{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/eBRPvWB.png)\n",
    "\n",
    "# Practical PyTorch: Translation with a Sequence to Sequence Network and Attention\n",
    "\n",
    "In this project we will be teaching a neural network to translate from French to English.\n",
    "\n",
    "```\n",
    "[KEY: > input, = target, < output]\n",
    "\n",
    "> il est en train de peindre un tableau .\n",
    "= he is painting a picture .\n",
    "< he is painting a picture .\n",
    "\n",
    "> pourquoi ne pas essayer ce vin delicieux ?\n",
    "= why not try that delicious wine ?\n",
    "< why not try that delicious wine ?\n",
    "\n",
    "> elle n est pas poete mais romanciere .\n",
    "= she is not a poet but a novelist .\n",
    "< she not not a poet but a novelist .\n",
    "\n",
    "> vous etes trop maigre .\n",
    "= you re too skinny .\n",
    "< you re all alone .\n",
    "```\n",
    "\n",
    "... to varying degrees of success.\n",
    "\n",
    "This is made possible by the simple but powerful idea of the [sequence to sequence network](http://arxiv.org/abs/1409.3215), in which two recurrent neural networks work together to transform one sequence to another. An encoder network condenses an input sequence into a single vector, and a decoder network unfolds that vector into a new sequence.\n",
    "\n",
    "To improve upon this model we'll use an [attention mechanism](https://arxiv.org/abs/1409.0473), which lets the decoder learn to focus over a specific range of the input sequence."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sequence to Sequence Learning\n",
    "\n",
    "A [Sequence to Sequence network](http://arxiv.org/abs/1409.3215), or seq2seq network, or [Encoder Decoder network](https://arxiv.org/pdf/1406.1078v3.pdf), is a model consisting of two separate RNNs called the **encoder** and **decoder**. The encoder reads an input sequence one item at a time, and outputs a vector at each step. The final output of the encoder is kept as the **context** vector. The decoder uses this context vector to produce a sequence of outputs one step at a time.\n",
    "\n",
    "![](https://i.imgur.com/tVtHhNp.png)\n",
    "\n",
    "When using a single RNN, there is a one-to-one relationship between inputs and outputs. We would quickly run into problems with different sequence orders and lengths that are common during translation. Consider the simple sentence \"Je ne suis pas le chat noir\" &rarr; \"I am not the black cat\". Many of the words have a pretty direct translation, like \"chat\" &rarr; \"cat\". However the differing grammars cause words to be in different orders, e.g. \"chat noir\" and \"black cat\". There is also the \"ne ... pas\" &rarr; \"not\" construction that makes the two sentences have different lengths.\n",
    "\n",
    "With the seq2seq model, by encoding many inputs into one vector, and decoding from one vector into many outputs, we are freed from the constraints of sequence order and length. The encoded sequence is represented by a single vector, a single point in some N dimensional space of sequences. In an ideal case, this point can be considered the \"meaning\" of the sequence.\n",
    "\n",
    "This idea can be extended beyond sequences. Image captioning tasks take an [image as input, and output a description](https://arxiv.org/abs/1411.4555) of the image (img2seq). Some image generation tasks take a [description as input and output a generated image](https://arxiv.org/abs/1511.02793) (seq2img). These models can be referred to more generally as \"encoder decoder\" networks."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Attention Mechanism\n",
    "\n",
    "The fixed-length vector carries the burden of encoding the the entire \"meaning\" of the input sequence, no matter how long that may be. With all the variance in language, this is a very hard problem. Imagine two nearly identical sentences, twenty words long, with only one word different. Both the encoders and decoders must be nuanced enough to represent that change as a very slightly different point in space.\n",
    "\n",
    "The **attention mechanism** [introduced by Bahdanau et al.](https://arxiv.org/abs/1409.0473) addresses this by giving the decoder a way to \"pay attention\" to parts of the input, rather than relying on a single vector. For every step the decoder can select a different part of the input sentence to consider.\n",
    "\n",
    "![](https://i.imgur.com/5y6SCvU.png)\n",
    "\n",
    "Attention is calculated using the current hidden state and each encoder output, resulting in a vector the same size as the input sequence, called the *attention weights*. These weights are multiplied by the encoder outputs to create a weighted sum of encoder outputs, which is called the *context* vector. The context vector and hidden state are used to predict the next output element.\n",
    "\n",
    "![](https://i.imgur.com/K1qMPxs.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Requirements\n",
    "\n",
    "You will need [PyTorch](http://pytorch.org/) to build and train the models, and [matplotlib](https://matplotlib.org/) for plotting training and visualizing attention outputs later. The rest are builtin Python libraries."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import unicodedata\n",
    "import string\n",
    "import re\n",
    "import random\n",
    "import time\n",
    "import datetime\n",
    "import math\n",
    "import socket\n",
    "hostname = socket.gethostname()\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.autograd import Variable\n",
    "from torch import optim\n",
    "import torch.nn.functional as F\n",
    "from torch.nn.utils.rnn import pad_packed_sequence, pack_padded_sequence#, masked_cross_entropy\n",
    "from masked_cross_entropy import *\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.ticker as ticker\n",
    "import numpy as np\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we will also define a constant to decide whether to use the GPU (with CUDA specifically) or the CPU. **If you don't have a GPU, set this to `False`**. Later when we create tensors, this variable will be used to decide whether we keep them on CPU or move them to GPU."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "USE_CUDA = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loading data files\n",
    "\n",
    "The data for this project is a set of many thousands of English to French translation pairs.\n",
    "\n",
    "[This question on Open Data Stack Exchange](http://opendata.stackexchange.com/questions/3888/dataset-of-sentences-translated-into-many-languages) pointed me to the open translation site http://tatoeba.org/ which has downloads available at http://tatoeba.org/eng/downloads - and better yet, someone did the extra work of splitting language pairs into individual text files here: http://www.manythings.org/anki/\n",
    "\n",
    "The English to French pairs are too big to include in the repo, so download `fra-eng.zip`, extract the text file in there, and rename it to `data/eng-fra.txt` before continuing (for some reason the zipfile is named backwards). The file is a tab separated list of translation pairs:\n",
    "\n",
    "```\n",
    "I am cold.    Je suis froid.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similar to the character encoding used in the character-level RNN tutorials, we will be representing each word in a language as a one-hot vector, or giant vector of zeros except for a single one (at the index of the word). Compared to the dozens of characters that might exist in a language, there are many many more words, so the encoding vector is much larger. We will however cheat a bit and trim the data to only use a few thousand words per language."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Indexing words\n",
    "\n",
    "We'll need a unique index per word to use as the inputs and targets of the networks later. To keep track of all this we will use a helper class called `Lang` which has word &rarr; index (`word2index`) and index &rarr; word (`index2word`) dictionaries, as well as a count of each word (`word2count`). This class includes a function `trim(min_count)` to remove rare words once they are all counted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "PAD_token = 0\n",
    "SOS_token = 1\n",
    "EOS_token = 2\n",
    "\n",
    "class Lang:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.trimmed = False\n",
    "        self.word2index = {}\n",
    "        self.word2count = {}\n",
    "        self.index2word = {0: \"PAD\", 1: \"SOS\", 2: \"EOS\"}\n",
    "        self.n_words = 3 # Count default tokens\n",
    "\n",
    "    def index_words(self, sentence):\n",
    "        for word in sentence.split(' '):\n",
    "            self.index_word(word)\n",
    "\n",
    "    def index_word(self, word):\n",
    "        if word not in self.word2index:\n",
    "            self.word2index[word] = self.n_words\n",
    "            self.word2count[word] = 1\n",
    "            self.index2word[self.n_words] = word\n",
    "            self.n_words += 1\n",
    "        else:\n",
    "            self.word2count[word] += 1\n",
    "\n",
    "    # Remove words below a certain count threshold\n",
    "    def trim(self, min_count):\n",
    "        if self.trimmed: return\n",
    "        self.trimmed = True\n",
    "        \n",
    "        keep_words = []\n",
    "        \n",
    "        for k, v in self.word2count.items():\n",
    "            if v >= min_count:\n",
    "                keep_words.append(k)\n",
    "\n",
    "        print('keep_words %s / %s = %.4f' % (\n",
    "            len(keep_words), len(self.word2index), len(keep_words) / len(self.word2index)\n",
    "        ))\n",
    "\n",
    "        # Reinitialize dictionaries\n",
    "        self.word2index = {}\n",
    "        self.word2count = {}\n",
    "        self.index2word = {0: \"PAD\", 1: \"SOS\", 2: \"EOS\"}\n",
    "        self.n_words = 3 # Count default tokens\n",
    "\n",
    "        for word in keep_words:\n",
    "            self.index_word(word)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Reading and decoding files\n",
    "\n",
    "The files are all in Unicode, to simplify we will turn Unicode characters to ASCII, make everything lowercase, and trim most punctuation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Turn a Unicode string to plain ASCII, thanks to http://stackoverflow.com/a/518232/2809427\n",
    "def unicode_to_ascii(s):\n",
    "    return ''.join(\n",
    "        c for c in unicodedata.normalize('NFD', s)\n",
    "        if unicodedata.category(c) != 'Mn'\n",
    "    )\n",
    "\n",
    "# Lowercase, trim, and remove non-letter characters\n",
    "def normalize_string(s):\n",
    "    s = unicode_to_ascii(s.lower().strip())\n",
    "    s = re.sub(r\"([,.!?])\", r\" \\1 \", s)\n",
    "    s = re.sub(r\"[^a-zA-Z,.!?]+\", r\" \", s)\n",
    "    s = re.sub(r\"\\s+\", r\" \", s).strip()\n",
    "    return s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To read the data file we will split the file into lines, and then split lines into pairs. The files are all English &rarr; Other Language, so if we want to translate from Other Language &rarr; English I added the `reverse` flag to reverse the pairs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def read_langs(lang1, lang2, reverse=False):\n",
    "    print(\"Reading lines...\")\n",
    "\n",
    "    # Read the file and split into lines\n",
    "#     filename = '../data/%s-%s.txt' % (lang1, lang2)\n",
    "    filename = '../%s-%s.txt' % (lang1, lang2)\n",
    "    lines = open(filename).read().strip().split('\\n')\n",
    "\n",
    "    # Split every line into pairs and normalize\n",
    "    pairs = [[normalize_string(s) for s in l.split('\\t')] for l in lines]\n",
    "\n",
    "    # Reverse pairs, make Lang instances\n",
    "    if reverse:\n",
    "        pairs = [list(reversed(p)) for p in pairs]\n",
    "        input_lang = Lang(lang2)\n",
    "        output_lang = Lang(lang1)\n",
    "    else:\n",
    "        input_lang = Lang(lang1)\n",
    "        output_lang = Lang(lang2)\n",
    "\n",
    "    return input_lang, output_lang, pairs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "MIN_LENGTH = 3\n",
    "MAX_LENGTH = 25\n",
    "\n",
    "def filter_pairs(pairs):\n",
    "    filtered_pairs = []\n",
    "    for pair in pairs:\n",
    "        if len(pair[0]) >= MIN_LENGTH and len(pair[0]) <= MAX_LENGTH \\\n",
    "            and len(pair[1]) >= MIN_LENGTH and len(pair[1]) <= MAX_LENGTH:\n",
    "                filtered_pairs.append(pair)\n",
    "    return filtered_pairs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The full process for preparing the data is:\n",
    "\n",
    "* Read text file and split into lines\n",
    "* Split lines into pairs and normalize\n",
    "* Filter to pairs of a certain length\n",
    "* Make word lists from sentences in pairs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reading lines...\n",
      "Read 135646 sentence pairs\n",
      "Filtered to 25706 pairs\n",
      "Indexing words...\n",
      "Indexed 6999 words in input language, 4343 words in output\n"
     ]
    }
   ],
   "source": [
    "def prepare_data(lang1_name, lang2_name, reverse=False):\n",
    "    input_lang, output_lang, pairs = read_langs(lang1_name, lang2_name, reverse)\n",
    "    print(\"Read %d sentence pairs\" % len(pairs))\n",
    "    \n",
    "    pairs = filter_pairs(pairs)\n",
    "    print(\"Filtered to %d pairs\" % len(pairs))\n",
    "    \n",
    "    print(\"Indexing words...\")\n",
    "    for pair in pairs:\n",
    "        input_lang.index_words(pair[0])\n",
    "        output_lang.index_words(pair[1])\n",
    "    \n",
    "    print('Indexed %d words in input language, %d words in output' % (input_lang.n_words, output_lang.n_words))\n",
    "    return input_lang, output_lang, pairs\n",
    "\n",
    "input_lang, output_lang, pairs = prepare_data('eng', 'fra', True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filtering vocabularies\n",
    "\n",
    "To get something that trains in under an hour, we'll trim the data set a bit. First we will use the `trim` function on each language (defined above) to only include words that are repeated a certain amount of times through the dataset (this softens the difficulty of learning a correct translation for words that don't appear often)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "keep_words 1717 / 6996 = 0.2454\n",
      "keep_words 1529 / 4340 = 0.3523\n"
     ]
    }
   ],
   "source": [
    "MIN_COUNT = 5\n",
    "\n",
    "input_lang.trim(MIN_COUNT)\n",
    "output_lang.trim(MIN_COUNT)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filtering pairs\n",
    "\n",
    "Now we will go back to the set of all sentence pairs and remove those with unknown words."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Trimmed from 25706 pairs to 15896, 0.6184 of total\n"
     ]
    }
   ],
   "source": [
    "keep_pairs = []\n",
    "\n",
    "for pair in pairs:\n",
    "    input_sentence = pair[0]\n",
    "    output_sentence = pair[1]\n",
    "    keep_input = True\n",
    "    keep_output = True\n",
    "    \n",
    "    for word in input_sentence.split(' '):\n",
    "        if word not in input_lang.word2index:\n",
    "            keep_input = False\n",
    "            break\n",
    "\n",
    "    for word in output_sentence.split(' '):\n",
    "        if word not in output_lang.word2index:\n",
    "            keep_output = False\n",
    "            break\n",
    "\n",
    "    # Remove if pair doesn't match input and output conditions\n",
    "    if keep_input and keep_output:\n",
    "        keep_pairs.append(pair)\n",
    "\n",
    "print(\"Trimmed from %d pairs to %d, %.4f of total\" % (len(pairs), len(keep_pairs), len(keep_pairs) / len(pairs)))\n",
    "pairs = keep_pairs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Turning training data into Tensors\n",
    "\n",
    "To train we need to turn the sentences into something the neural network can understand, which of course means numbers. Each sentence will be split into words and turned into a `LongTensor` which represents the index (from the Lang indexes made earlier) of each word. While creating these tensors we will also append the EOS token to signal that the sentence is over.\n",
    "\n",
    "![](https://i.imgur.com/LzocpGH.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Return a list of indexes, one for each word in the sentence, plus EOS\n",
    "def indexes_from_sentence(lang, sentence):\n",
    "    return [lang.word2index[word] for word in sentence.split(' ')] + [EOS_token]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can make better use of the GPU by training on batches of many sequences at once, but doing so brings up the question of how to deal with sequences of varying lengths. The simple solution is to \"pad\" the shorter sentences with some padding symbol (in this case `0`), and ignore these padded spots when calculating the loss.\n",
    "\n",
    "![](https://i.imgur.com/gGlkEEF.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Pad a with the PAD symbol\n",
    "def pad_seq(seq, max_length):\n",
    "    seq += [PAD_token for i in range(max_length - len(seq))]\n",
    "    return seq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To create a Variable for a full batch of inputs (and targets) we get a random sample of sequences and pad them all to the length of the longest sequence. We'll keep track of the lengths of each batch in order to un-pad later.\n",
    "\n",
    "Initializing a `LongTensor` with an array (batches) of arrays (sequences) gives us a `(batch_size x max_len)` tensor - selecting the first dimension gives you a single batch, which is a full sequence. When training the model we'll want a single time step at once, so we'll transpose to `(max_len x batch_size)`. Now selecting along the first dimension returns a single time step across batches.\n",
    "\n",
    "![](https://i.imgur.com/nBxTG3v.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def random_batch(batch_size):\n",
    "    input_seqs = []\n",
    "    target_seqs = []\n",
    "\n",
    "    # Choose random pairs\n",
    "    for i in range(batch_size):\n",
    "        pair = random.choice(pairs)\n",
    "        input_seqs.append(indexes_from_sentence(input_lang, pair[0]))\n",
    "        target_seqs.append(indexes_from_sentence(output_lang, pair[1]))\n",
    "\n",
    "    # Zip into pairs, sort by length (descending), unzip\n",
    "    seq_pairs = sorted(zip(input_seqs, target_seqs), key=lambda p: len(p[0]), reverse=True)\n",
    "    input_seqs, target_seqs = zip(*seq_pairs)\n",
    "    \n",
    "    # For input and target sequences, get array of lengths and pad with 0s to max length\n",
    "    input_lengths = [len(s) for s in input_seqs]\n",
    "    input_padded = [pad_seq(s, max(input_lengths)) for s in input_seqs]\n",
    "    target_lengths = [len(s) for s in target_seqs]\n",
    "    target_padded = [pad_seq(s, max(target_lengths)) for s in target_seqs]\n",
    "\n",
    "    # Turn padded arrays into (batch_size x max_len) tensors, transpose into (max_len x batch_size)\n",
    "    input_var = Variable(torch.LongTensor(input_padded)).transpose(0, 1)\n",
    "    target_var = Variable(torch.LongTensor(target_padded)).transpose(0, 1)\n",
    "    \n",
    "    if USE_CUDA:\n",
    "        input_var = input_var.cuda()\n",
    "        target_var = target_var.cuda()\n",
    "        \n",
    "    return input_var, input_lengths, target_var, target_lengths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can test this to see that it will return a `(max_len x batch_size)` tensor for input and target sentences, along with a corresponding list of batch lenghts for each (which we will use for masking later)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Variable containing:\n",
       "   88   92\n",
       "   44  208\n",
       "  107  297\n",
       "  634   14\n",
       "   14    2\n",
       "    2    0\n",
       " [torch.cuda.LongTensor of size 6x2 (GPU 0)], [6, 5], Variable containing:\n",
       "    50    50\n",
       "  1128    19\n",
       "   436    26\n",
       "   969     4\n",
       "     4     2\n",
       "     2     0\n",
       " [torch.cuda.LongTensor of size 6x2 (GPU 0)], [6, 5])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random_batch(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building the models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Encoder\n",
    "\n",
    "<img src=\"images/encoder-network.png\" style=\"float: right\" />\n",
    "\n",
    "The encoder will take a batch of word sequences, a `LongTensor` of size `(max_len x batch_size)`, and output an encoding for each word, a `FloatTensor` of size `(max_len x batch_size x hidden_size)`.\n",
    "\n",
    "The word inputs are fed through an [embedding layer `nn.Embedding`](http://pytorch.org/docs/nn.html#embedding) to create an embedding for each word, with size `seq_len x hidden_size` (as if it was a batch of words). This is resized to `seq_len x 1 x hidden_size` to fit the expected input of the [GRU layer `nn.GRU`](http://pytorch.org/docs/nn.html#gru). The GRU will return both an output sequence of size `seq_len x hidden_size`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class EncoderRNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, n_layers=1, dropout=0.1):\n",
    "        super(EncoderRNN, self).__init__()\n",
    "        \n",
    "        self.input_size = input_size\n",
    "        self.hidden_size = hidden_size\n",
    "        self.n_layers = n_layers\n",
    "        self.dropout = dropout\n",
    "        \n",
    "        self.embedding = nn.Embedding(input_size, hidden_size)\n",
    "        self.gru = nn.GRU(hidden_size, hidden_size, n_layers, dropout=self.dropout, bidirectional=True)\n",
    "        \n",
    "    def forward(self, input_seqs, input_lengths, hidden=None):\n",
    "        # Note: we run this all at once (over multiple batches of multiple sequences)\n",
    "        embedded = self.embedding(input_seqs)\n",
    "        packed = torch.nn.utils.rnn.pack_padded_sequence(embedded, input_lengths)\n",
    "        outputs, hidden = self.gru(packed, hidden)\n",
    "        outputs, output_lengths = torch.nn.utils.rnn.pad_packed_sequence(outputs) # unpack (back to padded)\n",
    "        outputs = outputs[:, :, :self.hidden_size] + outputs[:, : ,self.hidden_size:] # Sum bidirectional outputs\n",
    "        return outputs, hidden"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attention Decoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpreting the Bahdanau et al. model\n",
    "\n",
    "[Neural Machine Translation by Jointly Learning to Align and Translate](https://arxiv.org/abs/1409.0473) (Dzmitry Bahdanau, Kyunghyun Cho, Yoshua Bengio) introduced the idea of using attention for seq2seq translation.\n",
    "\n",
    "Each decoder output is conditioned on the previous outputs and some $\\mathbf x$, where $\\mathbf x$ consists of the current hidden state (which takes into account previous outputs) and the attention \"context\", which is calculated below. The function $g$ is a fully-connected layer with a nonlinear activation, which takes as input the values $y_{i-1}$, $s_i$, and $c_i$ concatenated.\n",
    "\n",
    "$$\n",
    "p(y_i \\mid \\{y_1,...,y_{i-1}\\},\\mathbf{x}) = g(y_{i-1}, s_i, c_i)\n",
    "$$\n",
    "\n",
    "The current hidden state $s_i$ is calculated by an RNN $f$ with the last hidden state $s_{i-1}$, last decoder output value $y_{i-1}$, and context vector $c_i$.\n",
    "\n",
    "In the code, the RNN will be a `nn.GRU` layer, the hidden state $s_i$ will be called `hidden`, the output $y_i$ called `output`, and context $c_i$ called `context`.\n",
    "\n",
    "$$\n",
    "s_i = f(s_{i-1}, y_{i-1}, c_i)\n",
    "$$\n",
    "\n",
    "The context vector $c_i$ is a weighted sum of all encoder outputs, where each weight $a_{ij}$ is the amount of \"attention\" paid to the corresponding encoder output $h_j$.\n",
    "\n",
    "$$\n",
    "c_i = \\sum_{j=1}^{T_x} a_{ij} h_j\n",
    "$$\n",
    "\n",
    "... where each weight $a_{ij}$ is a normalized (over all steps) attention \"energy\" $e_{ij}$ ...\n",
    "\n",
    "$$\n",
    "a_{ij} = \\dfrac{exp(e_{ij})}{\\sum_{k=1}^{T} exp(e_{ik})}\n",
    "$$\n",
    "\n",
    "... where each attention energy is calculated with some function $a$ (such as another linear layer) using the last hidden state $s_{i-1}$ and that particular encoder output $h_j$:\n",
    "\n",
    "$$\n",
    "e_{ij} = a(s_{i-1}, h_j)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpreting the Luong et al. models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Effective Approaches to Attention-based Neural Machine Translation](https://arxiv.org/abs/1508.04025) (Minh-Thang Luong, Hieu Pham, Christopher D. Manning) describe a few more attention models that offer improvements and simplifications. They describe a few \"global attention\" models, the distinction between them being the way the attention scores are calculated.\n",
    "\n",
    "The general form of the attention calculation relies on the target (decoder) side hidden state and corresponding source (encoder) side state, normalized over all states to get values summing to 1:\n",
    "\n",
    "$$\n",
    "a_t(s) = align(h_t, \\bar h_s)  = \\dfrac{exp(score(h_t, \\bar h_s))}{\\sum_{s'} exp(score(h_t, \\bar h_{s'}))}\n",
    "$$\n",
    "\n",
    "The specific \"score\" function that compares two states is either *dot*, a simple dot product between the states; *general*, a a dot product between the decoder hidden state and a linear transform of the encoder state; or *concat*, a dot product between a new parameter $v_a$ and a linear transform of the states concatenated together.\n",
    "\n",
    "$$\n",
    "score(h_t, \\bar h_s) =\n",
    "\\begin{cases}\n",
    "h_t ^\\top \\bar h_s & dot \\\\\n",
    "h_t ^\\top \\textbf{W}_a \\bar h_s & general \\\\\n",
    "v_a ^\\top \\textbf{W}_a [ h_t ; \\bar h_s ] & concat\n",
    "\\end{cases}\n",
    "$$\n",
    "\n",
    "The modular definition of these scoring functions gives us an opportunity to build specific attention module that can switch between the different score methods. The input to this module is always the hidden state (of the decoder RNN) and set of encoder outputs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementing an attention module"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Attn(nn.Module):\n",
    "    def __init__(self, method, hidden_size):\n",
    "        super(Attn, self).__init__()\n",
    "        \n",
    "        self.method = method\n",
    "        self.hidden_size = hidden_size\n",
    "        \n",
    "        if self.method == 'general':\n",
    "            self.attn = nn.Linear(self.hidden_size, hidden_size)\n",
    "\n",
    "        elif self.method == 'concat':\n",
    "            self.attn = nn.Linear(self.hidden_size * 2, hidden_size)\n",
    "            self.v = nn.Parameter(torch.FloatTensor(1, hidden_size))\n",
    "\n",
    "    def forward(self, hidden, encoder_outputs):\n",
    "        max_len = encoder_outputs.size(0)\n",
    "        this_batch_size = encoder_outputs.size(1)\n",
    "\n",
    "        # Create variable to store attention energies\n",
    "        attn_energies = Variable(torch.zeros(this_batch_size, max_len)) # B x S\n",
    "\n",
    "        if USE_CUDA:\n",
    "            attn_energies = attn_energies.cuda()\n",
    "\n",
    "        # For each batch of encoder outputs\n",
    "        for b in range(this_batch_size):\n",
    "            # Calculate energy for each encoder output\n",
    "            for i in range(max_len):\n",
    "                attn_energies[b, i] = self.score(hidden[:, b], encoder_outputs[i, b].unsqueeze(0))\n",
    "\n",
    "        # Normalize energies to weights in range 0 to 1, resize to 1 x B x S\n",
    "        return F.softmax(attn_energies).unsqueeze(1)\n",
    "    \n",
    "    def score(self, hidden, encoder_output):\n",
    "        \n",
    "        if self.method == 'dot':\n",
    "            energy = hidden.dot(encoder_output)\n",
    "            return energy\n",
    "        \n",
    "        elif self.method == 'general':\n",
    "            energy = self.attn(encoder_output)\n",
    "            energy = hidden.dot(energy)\n",
    "            return energy\n",
    "        \n",
    "        elif self.method == 'concat':\n",
    "            energy = self.attn(torch.cat((hidden, encoder_output), 1))\n",
    "            energy = self.v.dot(energy)\n",
    "            return energy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementing the Bahdanau et al. model\n",
    "\n",
    "In summary our decoder should consist of four main parts - an embedding layer turning an input word into a vector; a layer to calculate the attention energy per encoder output; a RNN layer; and an output layer.\n",
    "\n",
    "The decoder's inputs are the last RNN hidden state $s_{i-1}$, last output $y_{i-1}$, and all encoder outputs $h_*$.\n",
    "\n",
    "* embedding layer with inputs $y_{i-1}$\n",
    "    * `embedded = embedding(last_rnn_output)`\n",
    "* attention layer $a$ with inputs $(s_{i-1}, h_j)$ and outputs $e_{ij}$, normalized to create $a_{ij}$\n",
    "    * `attn_energies[j] = attn_layer(last_hidden, encoder_outputs[j])`\n",
    "    * `attn_weights = normalize(attn_energies)`\n",
    "* context vector $c_i$ as an attention-weighted average of encoder outputs\n",
    "    * `context = sum(attn_weights * encoder_outputs)`\n",
    "* RNN layer(s) $f$ with inputs $(s_{i-1}, y_{i-1}, c_i)$ and internal hidden state, outputting $s_i$\n",
    "    * `rnn_input = concat(embedded, context)`\n",
    "    * `rnn_output, rnn_hidden = rnn(rnn_input, last_hidden)`\n",
    "* an output layer $g$ with inputs $(y_{i-1}, s_i, c_i)$, outputting $y_i$\n",
    "    * `output = out(embedded, rnn_output, context)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class BahdanauAttnDecoderRNN(nn.Module):\n",
    "    def __init__(self, hidden_size, output_size, n_layers=1, dropout_p=0.1):\n",
    "        super(BahdanauAttnDecoderRNN, self).__init__()\n",
    "        \n",
    "        # Define parameters\n",
    "        self.hidden_size = hidden_size\n",
    "        self.output_size = output_size\n",
    "        self.n_layers = n_layers\n",
    "        self.dropout_p = dropout_p\n",
    "        self.max_length = max_length\n",
    "        \n",
    "        # Define layers\n",
    "        self.embedding = nn.Embedding(output_size, hidden_size)\n",
    "        self.dropout = nn.Dropout(dropout_p)\n",
    "        self.attn = Attn('concat', hidden_size)\n",
    "        self.gru = nn.GRU(hidden_size, hidden_size, n_layers, dropout=dropout_p)\n",
    "        self.out = nn.Linear(hidden_size, output_size)\n",
    "    \n",
    "    def forward(self, word_input, last_hidden, encoder_outputs):\n",
    "        # Note: we run this one step at a time\n",
    "        # TODO: FIX BATCHING\n",
    "        \n",
    "        # Get the embedding of the current input word (last output word)\n",
    "        word_embedded = self.embedding(word_input).view(1, 1, -1) # S=1 x B x N\n",
    "        word_embedded = self.dropout(word_embedded)\n",
    "        \n",
    "        # Calculate attention weights and apply to encoder outputs\n",
    "        attn_weights = self.attn(last_hidden[-1], encoder_outputs)\n",
    "        context = attn_weights.bmm(encoder_outputs.transpose(0, 1)) # B x 1 x N\n",
    "        context = context.transpose(0, 1) # 1 x B x N\n",
    "        \n",
    "        # Combine embedded input word and attended context, run through RNN\n",
    "        rnn_input = torch.cat((word_embedded, context), 2)\n",
    "        output, hidden = self.gru(rnn_input, last_hidden)\n",
    "        \n",
    "        # Final output layer\n",
    "        output = output.squeeze(0) # B x N\n",
    "        output = F.log_softmax(self.out(torch.cat((output, context), 1)))\n",
    "        \n",
    "        # Return final output, hidden state, and attention weights (for visualization)\n",
    "        return output, hidden, attn_weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can build a decoder that plugs this Attn module in after the RNN to calculate attention weights, and apply those weights to the encoder outputs to get a context vector."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class LuongAttnDecoderRNN(nn.Module):\n",
    "    def __init__(self, attn_model, hidden_size, output_size, n_layers=1, dropout=0.1):\n",
    "        super(LuongAttnDecoderRNN, self).__init__()\n",
    "\n",
    "        # Keep for reference\n",
    "        self.attn_model = attn_model\n",
    "        self.hidden_size = hidden_size\n",
    "        self.output_size = output_size\n",
    "        self.n_layers = n_layers\n",
    "        self.dropout = dropout\n",
    "\n",
    "        # Define layers\n",
    "        self.embedding = nn.Embedding(output_size, hidden_size)\n",
    "        self.embedding_dropout = nn.Dropout(dropout)\n",
    "        self.gru = nn.GRU(hidden_size, hidden_size, n_layers, dropout=dropout)\n",
    "        self.concat = nn.Linear(hidden_size * 2, hidden_size)\n",
    "        self.out = nn.Linear(hidden_size, output_size)\n",
    "        \n",
    "        # Choose attention model\n",
    "        if attn_model != 'none':\n",
    "            self.attn = Attn(attn_model, hidden_size)\n",
    "\n",
    "    def forward(self, input_seq, last_hidden, encoder_outputs):\n",
    "        # Note: we run this one step at a time\n",
    "\n",
    "        # Get the embedding of the current input word (last output word)\n",
    "        batch_size = input_seq.size(0)\n",
    "        embedded = self.embedding(input_seq)\n",
    "        embedded = self.embedding_dropout(embedded)\n",
    "        embedded = embedded.view(1, batch_size, self.hidden_size) # S=1 x B x N\n",
    "\n",
    "        # Get current hidden state from input word and last hidden state\n",
    "        rnn_output, hidden = self.gru(embedded, last_hidden)\n",
    "\n",
    "        # Calculate attention from current RNN state and all encoder outputs;\n",
    "        # apply to encoder outputs to get weighted average\n",
    "        attn_weights = self.attn(rnn_output, encoder_outputs)\n",
    "        context = attn_weights.bmm(encoder_outputs.transpose(0, 1)) # B x S=1 x N\n",
    "\n",
    "        # Attentional vector using the RNN hidden state and context vector\n",
    "        # concatenated together (Luong eq. 5)\n",
    "        rnn_output = rnn_output.squeeze(0) # S=1 x B x N -> B x N\n",
    "        context = context.squeeze(1)       # B x S=1 x N -> B x N\n",
    "        concat_input = torch.cat((rnn_output, context), 1)\n",
    "        concat_output = F.tanh(self.concat(concat_input))\n",
    "\n",
    "        # Finally predict next token (Luong eq. 6, without softmax)\n",
    "        output = self.out(concat_output)\n",
    "\n",
    "        # Return final output, hidden state, and attention weights (for visualization)\n",
    "        return output, hidden, attn_weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Testing the models\n",
    "\n",
    "To make sure the encoder and decoder modules are working (and working together) we'll do a full test with a small batch."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_batches torch.Size([7, 3])\n",
      "target_batches torch.Size([8, 3])\n"
     ]
    }
   ],
   "source": [
    "small_batch_size = 3\n",
    "input_batches, input_lengths, target_batches, target_lengths = random_batch(small_batch_size)\n",
    "\n",
    "print('input_batches', input_batches.size()) # (max_len x batch_size)\n",
    "print('target_batches', target_batches.size()) # (max_len x batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create models with a small size (a good idea for eyeball inspection):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "small_hidden_size = 8\n",
    "small_n_layers = 2\n",
    "\n",
    "encoder_test = EncoderRNN(input_lang.n_words, small_hidden_size, small_n_layers)\n",
    "decoder_test = LuongAttnDecoderRNN('general', small_hidden_size, output_lang.n_words, small_n_layers)\n",
    "\n",
    "if USE_CUDA:\n",
    "    encoder_test.cuda()\n",
    "    decoder_test.cuda()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To test the encoder, run the input batch through to get per-batch encoder outputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "encoder_outputs torch.Size([7, 3, 8])\n",
      "encoder_hidden torch.Size([4, 3, 8])\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/sean/anaconda3/lib/python3.6/site-packages/torch/backends/cudnn/__init__.py:46: UserWarning: PyTorch was compiled without cuDNN support. To use cuDNN, rebuild PyTorch making sure the library is visible to the build system.\n",
      "  \"PyTorch was compiled without cuDNN support. To use cuDNN, rebuild \"\n"
     ]
    }
   ],
   "source": [
    "encoder_outputs, encoder_hidden = encoder_test(input_batches, input_lengths, None)\n",
    "\n",
    "print('encoder_outputs', encoder_outputs.size()) # max_len x batch_size x hidden_size\n",
    "print('encoder_hidden', encoder_hidden.size()) # n_layers * 2 x batch_size x hidden_size"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then starting with a SOS token, run word tokens through the decoder to get each next word token. Instead of doing this with the whole sequence, it is done one at a time, to support using it's own predictions to make the next prediction. This will be one time step at a time, but batched per time step. In order to get this to work for short padded sequences, the batch size is going to get smaller each time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss 7.343282222747803\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/sean/anaconda3/lib/python3.6/site-packages/torch/backends/cudnn/__init__.py:46: UserWarning: PyTorch was compiled without cuDNN support. To use cuDNN, rebuild PyTorch making sure the library is visible to the build system.\n",
      "  \"PyTorch was compiled without cuDNN support. To use cuDNN, rebuild \"\n",
      "/home/sean/Projects/practical-pytorch/seq2seq-translation/masked_cross_entropy.py:9: UserWarning: torch.range is deprecated in favor of torch.arange and will be removed in 0.3. Note that arange generates values in [start; end), not [start; end].\n",
      "  seq_range = torch.range(0, max_len - 1).long()\n"
     ]
    }
   ],
   "source": [
    "max_target_length = max(target_lengths)\n",
    "\n",
    "# Prepare decoder input and outputs\n",
    "decoder_input = Variable(torch.LongTensor([SOS_token] * small_batch_size))\n",
    "decoder_hidden = encoder_hidden[:decoder_test.n_layers] # Use last (forward) hidden state from encoder\n",
    "all_decoder_outputs = Variable(torch.zeros(max_target_length, small_batch_size, decoder_test.output_size))\n",
    "\n",
    "if USE_CUDA:\n",
    "    all_decoder_outputs = all_decoder_outputs.cuda()\n",
    "    decoder_input = decoder_input.cuda()\n",
    "\n",
    "# Run through decoder one time step at a time\n",
    "for t in range(max_target_length):\n",
    "    decoder_output, decoder_hidden, decoder_attn = decoder_test(\n",
    "        decoder_input, decoder_hidden, encoder_outputs\n",
    "    )\n",
    "    all_decoder_outputs[t] = decoder_output # Store this step's outputs\n",
    "    decoder_input = target_batches[t] # Next input is current target\n",
    "\n",
    "# Test masked cross entropy loss\n",
    "loss = masked_cross_entropy(\n",
    "    all_decoder_outputs.transpose(0, 1).contiguous(),\n",
    "    target_batches.transpose(0, 1).contiguous(),\n",
    "    target_lengths\n",
    ")\n",
    "print('loss', loss.data[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training\n",
    "\n",
    "## Defining a training iteration\n",
    "\n",
    "To train we first run the input sentence through the encoder word by word, and keep track of every output and the latest hidden state. Next the decoder is given the last hidden state of the decoder as its first hidden state, and the `<SOS>` token as its first input. From there we iterate to predict a next token from the decoder.\n",
    "\n",
    "### Teacher Forcing vs. Scheduled Sampling\n",
    "\n",
    "\"Teacher Forcing\", or maximum likelihood sampling, means using the real target outputs as each next input when training. The alternative is using the decoder's own guess as the next input. Using teacher forcing may cause the network to converge faster, but [when the trained network is exploited, it may exhibit instability](http://minds.jacobs-university.de/sites/default/files/uploads/papers/ESNTutorialRev.pdf).\n",
    "\n",
    "You can observe outputs of teacher-forced networks that read with coherent grammar but wander far from the correct translation - you could think of it as having learned how to listen to the teacher's instructions, without learning how to venture out on its own.\n",
    "\n",
    "The solution to the teacher-forcing \"problem\" is known as [Scheduled Sampling](https://arxiv.org/abs/1506.03099), which simply alternates between using the target values and predicted values when training. We will randomly choose to use teacher forcing with an if statement while training - sometimes we'll feed use real target as the input (ignoring the decoder's output), sometimes we'll use the decoder's output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def train(input_batches, input_lengths, target_batches, target_lengths, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH):\n",
    "    \n",
    "    # Zero gradients of both optimizers\n",
    "    encoder_optimizer.zero_grad()\n",
    "    decoder_optimizer.zero_grad()\n",
    "    loss = 0 # Added onto for each word\n",
    "\n",
    "    # Run words through encoder\n",
    "    encoder_outputs, encoder_hidden = encoder(input_batches, input_lengths, None)\n",
    "    \n",
    "    # Prepare input and output variables\n",
    "    decoder_input = Variable(torch.LongTensor([SOS_token] * batch_size))\n",
    "    decoder_hidden = encoder_hidden[:decoder.n_layers] # Use last (forward) hidden state from encoder\n",
    "\n",
    "    max_target_length = max(target_lengths)\n",
    "    all_decoder_outputs = Variable(torch.zeros(max_target_length, batch_size, decoder.output_size))\n",
    "\n",
    "    # Move new Variables to CUDA\n",
    "    if USE_CUDA:\n",
    "        decoder_input = decoder_input.cuda()\n",
    "        all_decoder_outputs = all_decoder_outputs.cuda()\n",
    "\n",
    "    # Run through decoder one time step at a time\n",
    "    for t in range(max_target_length):\n",
    "        decoder_output, decoder_hidden, decoder_attn = decoder(\n",
    "            decoder_input, decoder_hidden, encoder_outputs\n",
    "        )\n",
    "\n",
    "        all_decoder_outputs[t] = decoder_output\n",
    "        decoder_input = target_batches[t] # Next input is current target\n",
    "\n",
    "    # Loss calculation and backpropagation\n",
    "    loss = masked_cross_entropy(\n",
    "        all_decoder_outputs.transpose(0, 1).contiguous(), # -> batch x seq\n",
    "        target_batches.transpose(0, 1).contiguous(), # -> batch x seq\n",
    "        target_lengths\n",
    "    )\n",
    "    loss.backward()\n",
    "    \n",
    "    # Clip gradient norms\n",
    "    ec = torch.nn.utils.clip_grad_norm(encoder.parameters(), clip)\n",
    "    dc = torch.nn.utils.clip_grad_norm(decoder.parameters(), clip)\n",
    "\n",
    "    # Update parameters with optimizers\n",
    "    encoder_optimizer.step()\n",
    "    decoder_optimizer.step()\n",
    "    \n",
    "    return loss.data[0], ec, dc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Running training\n",
    "\n",
    "With everything in place we can actually initialize a network and start training.\n",
    "\n",
    "To start, we initialize models, optimizers, a loss function (criterion), and set up variables for plotting and tracking progress:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting job 59739ec4f8e1c2083c28a9f6 at 2017-07-22 20:11:42\n"
     ]
    }
   ],
   "source": [
    "# Configure models\n",
    "attn_model = 'dot'\n",
    "hidden_size = 500\n",
    "n_layers = 2\n",
    "dropout = 0.1\n",
    "batch_size = 100\n",
    "batch_size = 50\n",
    "\n",
    "# Configure training/optimization\n",
    "clip = 50.0\n",
    "teacher_forcing_ratio = 0.5\n",
    "learning_rate = 0.0001\n",
    "decoder_learning_ratio = 5.0\n",
    "n_epochs = 50000\n",
    "epoch = 0\n",
    "plot_every = 20\n",
    "print_every = 100\n",
    "evaluate_every = 1000\n",
    "\n",
    "# Initialize models\n",
    "encoder = EncoderRNN(input_lang.n_words, hidden_size, n_layers, dropout=dropout)\n",
    "decoder = LuongAttnDecoderRNN(attn_model, hidden_size, output_lang.n_words, n_layers, dropout=dropout)\n",
    "\n",
    "# Initialize optimizers and criterion\n",
    "encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)\n",
    "decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate * decoder_learning_ratio)\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "# Move models to GPU\n",
    "if USE_CUDA:\n",
    "    encoder.cuda()\n",
    "    decoder.cuda()\n",
    "\n",
    "import sconce\n",
    "job = sconce.Job('seq2seq-translate', {\n",
    "    'attn_model': attn_model,\n",
    "    'n_layers': n_layers,\n",
    "    'dropout': dropout,\n",
    "    'hidden_size': hidden_size,\n",
    "    'learning_rate': learning_rate,\n",
    "    'clip': clip,\n",
    "    'teacher_forcing_ratio': teacher_forcing_ratio,\n",
    "    'decoder_learning_ratio': decoder_learning_ratio,\n",
    "})\n",
    "job.plot_every = plot_every\n",
    "job.log_every = print_every\n",
    "\n",
    "# Keep track of time elapsed and running averages\n",
    "start = time.time()\n",
    "plot_losses = []\n",
    "print_loss_total = 0 # Reset every print_every\n",
    "plot_loss_total = 0 # Reset every plot_every"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Plus helper functions to print time elapsed and estimated time remaining, given the current time and progress."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def as_minutes(s):\n",
    "    m = math.floor(s / 60)\n",
    "    s -= m * 60\n",
    "    return '%dm %ds' % (m, s)\n",
    "\n",
    "def time_since(since, percent):\n",
    "    now = time.time()\n",
    "    s = now - since\n",
    "    es = s / (percent)\n",
    "    rs = es - s\n",
    "    return '%s (- %s)' % (as_minutes(s), as_minutes(rs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Evaluating the network\n",
    "\n",
    "Evaluation is mostly the same as training, but there are no targets. Instead we always feed the decoder's predictions back to itself. Every time it predicts a word, we add it to the output string. If it predicts the EOS token we stop there. We also store the decoder's attention outputs for each step to display later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def evaluate(input_seq, max_length=MAX_LENGTH):\n",
    "    input_lengths = [len(input_seq)]\n",
    "    input_seqs = [indexes_from_sentence(input_lang, input_seq)]\n",
    "    input_batches = Variable(torch.LongTensor(input_seqs), volatile=True).transpose(0, 1)\n",
    "    \n",
    "    if USE_CUDA:\n",
    "        input_batches = input_batches.cuda()\n",
    "        \n",
    "    # Set to not-training mode to disable dropout\n",
    "    encoder.train(False)\n",
    "    decoder.train(False)\n",
    "    \n",
    "    # Run through encoder\n",
    "    encoder_outputs, encoder_hidden = encoder(input_batches, input_lengths, None)\n",
    "\n",
    "    # Create starting vectors for decoder\n",
    "    decoder_input = Variable(torch.LongTensor([SOS_token]), volatile=True) # SOS\n",
    "    decoder_hidden = encoder_hidden[:decoder.n_layers] # Use last (forward) hidden state from encoder\n",
    "    \n",
    "    if USE_CUDA:\n",
    "        decoder_input = decoder_input.cuda()\n",
    "\n",
    "    # Store output words and attention states\n",
    "    decoded_words = []\n",
    "    decoder_attentions = torch.zeros(max_length + 1, max_length + 1)\n",
    "    \n",
    "    # Run through decoder\n",
    "    for di in range(max_length):\n",
    "        decoder_output, decoder_hidden, decoder_attention = decoder(\n",
    "            decoder_input, decoder_hidden, encoder_outputs\n",
    "        )\n",
    "        decoder_attentions[di,:decoder_attention.size(2)] += decoder_attention.squeeze(0).squeeze(0).cpu().data\n",
    "\n",
    "        # Choose top word from output\n",
    "        topv, topi = decoder_output.data.topk(1)\n",
    "        ni = topi[0][0]\n",
    "        if ni == EOS_token:\n",
    "            decoded_words.append('<EOS>')\n",
    "            break\n",
    "        else:\n",
    "            decoded_words.append(output_lang.index2word[ni])\n",
    "            \n",
    "        # Next input is chosen word\n",
    "        decoder_input = Variable(torch.LongTensor([ni]))\n",
    "        if USE_CUDA: decoder_input = decoder_input.cuda()\n",
    "\n",
    "    # Set back to training mode\n",
    "    encoder.train(True)\n",
    "    decoder.train(True)\n",
    "    \n",
    "    return decoded_words, decoder_attentions[:di+1, :len(encoder_outputs)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can evaluate random sentences from the training set and print out the input, target, and output to make some subjective quality judgements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def evaluate_randomly():\n",
    "    [input_sentence, target_sentence] = random.choice(pairs)\n",
    "    evaluate_and_show_attention(input_sentence, target_sentence)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualizing attention\n",
    "\n",
    "A useful property of the attention mechanism is its highly interpretable outputs. Because it is used to weight specific encoder outputs of the input sequence, we can imagine looking where the network is focused most at each time step.\n",
    "\n",
    "You could simply run `plt.matshow(attentions)` to see attention output displayed as a matrix, with the columns being input steps and rows being output steps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import io\n",
    "import torchvision\n",
    "from PIL import Image\n",
    "import visdom\n",
    "vis = visdom.Visdom()\n",
    "\n",
    "def show_plot_visdom():\n",
    "    buf = io.BytesIO()\n",
    "    plt.savefig(buf)\n",
    "    buf.seek(0)\n",
    "    attn_win = 'attention (%s)' % hostname\n",
    "    vis.image(torchvision.transforms.ToTensor()(Image.open(buf)), win=attn_win, opts={'title': attn_win})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For a better viewing experience we will do the extra work of adding axes and labels:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def show_attention(input_sentence, output_words, attentions):\n",
    "    # Set up figure with colorbar\n",
    "    fig = plt.figure()\n",
    "    ax = fig.add_subplot(111)\n",
    "    cax = ax.matshow(attentions.numpy(), cmap='bone')\n",
    "    fig.colorbar(cax)\n",
    "\n",
    "    # Set up axes\n",
    "    ax.set_xticklabels([''] + input_sentence.split(' ') + ['<EOS>'], rotation=90)\n",
    "    ax.set_yticklabels([''] + output_words)\n",
    "\n",
    "    # Show label at every tick\n",
    "    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n",
    "    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n",
    "\n",
    "    show_plot_visdom()\n",
    "    plt.show()\n",
    "    plt.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def evaluate_and_show_attention(input_sentence, target_sentence=None):\n",
    "    output_words, attentions = evaluate(input_sentence)\n",
    "    output_sentence = ' '.join(output_words)\n",
    "    print('>', input_sentence)\n",
    "    if target_sentence is not None:\n",
    "        print('=', target_sentence)\n",
    "    print('<', output_sentence)\n",
    "    \n",
    "    show_attention(input_sentence, output_words, attentions)\n",
    "    \n",
    "    # Show input, target, output text in visdom\n",
    "    win = 'evaluted (%s)' % hostname\n",
    "    text = '<p>&gt; %s</p><p>= %s</p><p>&lt; %s</p>' % (input_sentence, target_sentence, output_sentence)\n",
    "    vis.text(text, win=win, opts={'title': win})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Putting it all together\n",
    "\n",
    "**TODO** Run `train_epochs` for `n_epochs`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To actually train, we call the train function many times, printing a summary as we go.\n",
    "\n",
    "*Note:* If you're running this notebook you can **train, interrupt, evaluate, and come back to continue training**. Simply run the notebook starting from the following cell (running from the previous cell will reset the models)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/sean/anaconda3/lib/python3.6/site-packages/torch/backends/cudnn/__init__.py:46: UserWarning: PyTorch was compiled without cuDNN support. To use cuDNN, rebuild PyTorch making sure the library is visible to the build system.\n",
      "  \"PyTorch was compiled without cuDNN support. To use cuDNN, rebuild \"\n",
      "/home/sean/Projects/practical-pytorch/seq2seq-translation/masked_cross_entropy.py:9: UserWarning: torch.range is deprecated in favor of torch.arange and will be removed in 0.3. Note that arange generates values in [start; end), not [start; end].\n",
      "  seq_range = torch.range(0, max_len - 1).long()\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 1m 50s (100) 3.1331\n",
      "1m 50s (- 921m 56s) (100 0%) 3.8196\n",
      "[log] 3m 41s (200) 2.3766\n",
      "3m 41s (- 921m 4s) (200 0%) 2.7289\n",
      "[log] 5m 35s (300) 2.1629\n",
      "5m 35s (- 926m 34s) (300 0%) 2.2523\n",
      "[log] 7m 28s (400) 1.9996\n",
      "7m 28s (- 926m 21s) (400 0%) 1.9320\n",
      "[log] 9m 20s (500) 1.5955\n",
      "9m 20s (- 924m 47s) (500 1%) 1.6854\n",
      "[log] 11m 13s (600) 1.2429\n",
      "11m 13s (- 924m 11s) (600 1%) 1.4429\n",
      "[log] 13m 5s (700) 1.2304\n",
      "13m 5s (- 922m 26s) (700 1%) 1.2527\n",
      "[log] 14m 57s (800) 0.9507\n",
      "14m 57s (- 919m 49s) (800 1%) 1.1110\n",
      "[log] 16m 49s (900) 0.8307\n",
      "16m 49s (- 917m 34s) (900 1%) 0.9817\n",
      "[log] 18m 39s (1000) 0.7994\n",
      "18m 39s (- 914m 34s) (1000 2%) 0.8726\n",
      "> suis je en retard ?\n",
      "= am i late ?\n",
      "< am i late ? <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc642fe08d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 20m 29s (1100) 0.6578\n",
      "20m 29s (- 911m 11s) (1100 2%) 0.7791\n",
      "[log] 22m 19s (1200) 0.6510\n",
      "22m 19s (- 907m 38s) (1200 2%) 0.6962\n",
      "[log] 24m 10s (1300) 0.5559\n",
      "24m 10s (- 905m 40s) (1300 2%) 0.6159\n",
      "[log] 26m 0s (1400) 0.4897\n",
      "26m 0s (- 903m 1s) (1400 2%) 0.5736\n",
      "[log] 27m 50s (1500) 0.5131\n",
      "27m 50s (- 900m 5s) (1500 3%) 0.5190\n",
      "[log] 29m 38s (1600) 0.3948\n",
      "29m 38s (- 896m 52s) (1600 3%) 0.4632\n",
      "[log] 31m 27s (1700) 0.6653\n",
      "31m 27s (- 893m 44s) (1700 3%) 0.4410\n",
      "[log] 33m 15s (1800) 0.3286\n",
      "33m 15s (- 890m 39s) (1800 3%) 0.3999\n",
      "[log] 35m 5s (1900) 0.4149\n",
      "35m 5s (- 888m 17s) (1900 3%) 0.3684\n",
      "[log] 36m 54s (2000) 0.2788\n",
      "36m 54s (- 885m 52s) (2000 4%) 0.3466\n",
      "> honte a toi !\n",
      "= shame on you .\n",
      "< shame on you ! <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAUYAAAEZCAYAAADrD4zSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAF3BJREFUeJzt3X+0XWV95/H3h/gTZIA21GESaKgraoMShBCYKbZYxUlc\nOGitAloZGGm0Ay4dRoH2D+tY+wNpbWsFY0oj0h+wrAWMNgLahUoHKST8ThQnC0SCTjWISFHB3Pvp\nH3vfuM/OPffuk5y79znXz8u1F2f/uM/5Em++PM9+9n6+sk1ERPzEPl0HEBExapIYIyJqkhgjImqS\nGCMiapIYIyJqkhgjImqSGCMiapIYIyJqkhgjImqSGCPmGRWulfSLXccyrpIYI+afVwLHAmd3Hci4\nSmKMmH/eQpEUXy3paV0HM46SGCPmEUkLgSNsfxb4PPCajkMaS0mMEfPLm4Ery88fI8PpPZLEGDG/\n/A+KhIjt24BDJB3abUjjJ4kxYp6QdCDwYdsPVw6/C1jYUUhjS1motj9JzwYOs31f17FERHvSY+xD\n0quBO4Hryv2jJG3oNqqI6Un6TUlLy8+S9DFJ35d0t6SXdB3fuEli7O+9wErgewC27wQO7zKgiBm8\nA/h6+fl04EiK39fzgA91FNPYSmLs78e2H6sdy32HGFU7bf+4/HwycIXtR2x/Htivw7jGUhJjf1sk\nvRFYIGmppL8Abu46qIg+JiUdIulZwMspnmGc8uyOYhpbSYz9vR04AngS+DvgMYrhSsQoeg+wiWI4\nvcH2FgBJvwLc32FcYymz0n1Ier3tv5/tWMSoKF//29/2o5Vj+1H8Pf+37iIbP0mMfUi63fbRsx2L\nGBWSfg44h2KkA7AFuNT2v3YX1XjKC+Y1klYDrwIWSarO5v0HYGc3UUXMTNIvUdzyuRy4ojx8DPAv\nkt5k+/92Fds4So+xRtJy4CjgfRT3baY8DtxYHaZEjApJtwC/ZfuO2vGjgI/aPq6byMZTEmMfkp5e\nefwhYqRJ2mp72aDnYnqZle5vpaTPSfqapPslPSAps3sxqiTpoGkO/gz5ez6w3GPs76+A/wVsBiY6\njiViNn8K3CDpXcDt5bFjgIvKczGADKX7kPQvuS8T40TSycD5FLPSBrYCF9v+dKeBjaEkxj4k/RGw\nALia4iFvAGzf3veHImJeSGLsQ9KN0xy27V9tPZiIWUj6hO03lJ8vsn1B5dwNtl/ZXXTjJ/cY+7D9\nsq5jiBjA0srnk4ALKvsHtxzL2Eti7EPSAcDvAr9cHvoi8L5pVtyJBqZ6NJLuoXeVIlH0xI/sKLT5\nYqahX4aFA0pi7G89cC/whnL/zRS1NH6ts4jG29QCHCd3GsX8tW+5IO0+wLPLzyq3rK4zoNxj7EPS\nnbaPmu1YDE7ScykKwgPcavvbXcYzH/S5J75Lbg0NJj3G/n4o6QTb/wy73kX9YccxjT1JbwAuBr5A\n0Zv5C0nvtv3JTgMbc0l8w5UeYx/lO6YfBw4oDz0K/Hfbd3cX1fiTdBdw0lQvUdLBwOdtL+82svFX\nFm97vu27KscOAyZqlQNjFukx9vcV4APA84ADKRaqfQ0wkomxfB1sKfCsqWO2v9RdRH3tUxs6P0Je\nWRuWncDVko60/UR57DLgd4AkxgEkMfb3KYpCWLcz4r9Uks6mmNxYTFHZ8Hjgy8AoPnP5WUnXA1eW\n+6cCGzuMZ96w/WNJ11BMGH6s7C0ebHtTx6GNnSTG/hbbXtV1EA29g2Iy4xbbL5P0QuAPOo6pHwMf\nBU4o99dRJPIYjsso/kw/BpxR/jMGlCFMfzdLenHXQTT0I9s/ApD0TNtfBV7QcUz9nGT7atvnlds1\nwOqug5ovyv/vJen5wGnAX3cc0lhKj7Gm8gDy04CzyqXGnmS0H0TeLulA4Frgc5IeBR7sOKYekn4L\n+J/AL0iq3qfdHxjJ1aUlnVc/ZvuD5bnfsP037UfVyF9R9BzvycLKeyaz0jWSfn6m87ZHKuHUlVXh\nDgCus/1U1/FMKd8kOgj4Q+DCyqnHbX+3m6hmJul368ds/5/y3Fttf7T9qGYnaV/gW8DryrrSMaAk\nxoiImtxjjIioSWJsQNKarmNoapxihfGKd5xihfGLd09IWi/p25Lu7XNekj4kaZukuyU1Kn+cxNjM\nOP2CjVOsMF7xjlOsMH7x7onLgZkeq1tN8eLDUoo/j480aTSJMSLGVvl210yTd6cAV7hwC3CgpENm\na3dePa6zcOFCL1myZOjtHnbYYaxYsWKos1SbN28eZnM9JI3VjNo4xTtOscLcxGtbe/Pzq1at8o4d\nOxpdu3nz5i3AjyqH1tleN8DXLQIequxvL499a6YfmleJccmSJWzaNB5vP0l79bsVMbZ27NjR+O+p\npB/ZXjHHIe1mXiXGiBgPLT4m+DBwaGV/MQ3WPsg9xoholYGJyclG2xBsAM4oZ6ePBx6zPeMwGtJj\njIjWGQ+pDI2kK4ETgYWStlPUaXo6gO21FCs3vQrYBvwAOKtJu0mMEdEuw+SQRtK2T5/lvIFzBm03\niTEiWjfqryInMUZEqwxMJjFGRPRKjzEiosL2sGac50wSY0S0Lj3GiIiaYT2uM1eSGCOiVcXkS9dR\nzCyJMSJal6F0RERVJl8iInqZ9BgjInaTB7wjImrSY4yI6DG81XXmyl6vxyjp65IWDiOYiJj/XK6u\n02TrSnqMEdG6yRGflR6oxyhpP0n/KOkuSfdKOrU89XZJt0u6R9ILy2tXSvqypDsk3SzpBeXxMyVd\nK+lzZW/zXEnnldfdIulnyuueJ+k6SZsl3TTVbkSMt6nVdZpsXRl0KL0K+Kbt5bZfBFxXHt9h+2iK\nmq3vKo99FXip7ZcA7wH+oNLOi4BfA44Ffh/4QXndl4EzymvWAW+3fUzZ5qXTBSRpjaRNkjZ95zvf\nGfBfJyK6YLvR1pVBh9L3AH8i6SLgM7ZvKqvdXV2e30yR8AAOAD4uaSnFfySeXmnnRtuPA49Legz4\ndKX9IyU9B/gvwN9Xquk9c7qAylKK64ChlziNiDnQcW+wiYESo+2vSTqaoobC+yX9U3nqyfKfE5U2\nf48iAb5W0hLgC5Wmnqx8nqzsT5Y/vw/wPdtHDRJfRIyHUX9cZ9B7jP+JYtj7N8DFwNEzXH4APylT\neOYg32P7+8ADkl5ffq8kLR+kjYgYTQYm7EZbVwa9x/hi4FZJd1JU43r/DNd+APhDSXewZ7PfbwLe\nIukuYAtwyh60EREjaNTvMWrUu7SDWLFihTdt2tR1GI1U7p1GjBXbe/XL+6Lly/2JjRsbXXvE4sWb\nba/Ym+/bE3mOMSJa5fk2+RIRMQyjPlJNYoyI1iUxRkRUFLPSo/1KYBJjRLQuNV8iIqo6fhSniSTG\niGhVShtEREwjj+tERNSkxxgRUeGUT42I2N2o13xJYoyI1o364zp7XQwrImIQU7PSw1pdR9IqSfdJ\n2ibpwmnOHyDp02VJli2SzpqtzSTGiGjdsBKjpAXAJcBqYBlwuqRltcvOAbbaXg6cSFGF4BkztZuh\ndES0a7iTLyuBbbbvB5B0FcXarVur3wjsr2Ktv+cA3wV2ztRoEmNEtGrID3gvAh6q7G8Hjqtd82Fg\nA/BNYH/gVHvml7XnVWLcvHlzFoCdI6P+3Fldfg9G2wAPeC+UVF19el1ZAG8Q/xW4E/hV4HnA5yTd\nVJZQmda8SowRMR4GeFxnxywreD8MHFrZX8xPak1NOQv4Ixf/dd8m6QHghcCt/RrN5EtEtM5utjVw\nG7BU0uHlhMppFMPmqm8ALweQ9FzgBcD9MzWaHmNEtMoM711p2zslnQtcDywA1tveIult5fm1FKWc\nL5d0DyDgAts7Zmo3iTEi2jXkVwJtbwQ21o6trXz+JvDKQdpMYoyIVmXZsYiIaSQxRkTUZD3GiIge\nzuo6ERFVAzyK05kkxohoXRaqjYioGOZzjHMliTEiWpdZ6YiIqtSVjoiYRhJjRESvyYkkxoiIXYrH\ndZIYIyJ6JDFGRPTI5EtExG484oWlR2IFb0nnSbq33N4paYmkr0j6y7IO7A2Snt11nBGx96buMQ6r\nrvRc6DwxSjqGoibDccDxwG8CBwFLgUtsHwF8D3hdZ0FGxFB5crLR1pVRGEqfAFxj+wkASVcDLwUe\nsH1nec1mYMl0PyxpDbCmhTgjYkhG/BbjSCTGfp6sfJ4Aph1Kl6UU1wFIGvE/7ojAzj3GBm4CXiNp\nX0n7Aa8tj0XEPDXq9xg77zHavl3S5fykxutlwKPdRRQRcyk1Xxqy/UHgg7XDL6qc/+N2I4qIuZTE\nGBFRZeOJLFQbEdEjPcaIiJoRz4tJjBHRrky+RETUZdmxiIg6M5nJl4iIXukxRkRUZAXviIjpJDFG\nRPTyaN9iTGKMiPZlKB0RUWUz2eEitE0kMUZEq8bhAe9RWI8xIn6auCiG1WRrQtIqSfdJ2ibpwj7X\nnCjpzrKG1BdnazM9xoho35B6jJIWAJcAJwHbgdskbbC9tXLNgcClwCrb35D0c7O1mx5jRLSs2erd\nDYfbK4Fttu+3/RRwFXBK7Zo3Alfb/gaA7W/P1mgSY0S0bnLSjTZgoaRNla1e+G4R8FBlf3t5rOr5\nwEGSviBps6QzZosvQ+mIaJXLe4wN7bC9Yi+/8mnAMcDLKYrqfVnSLba/NtMPRES0aoiz0g8Dh1b2\nF5fHqrYDj5Qlmp+Q9CVgOdA3MWYoHRGtG+I9xtuApZIOl/QM4DRgQ+2aTwEnSHqapH2B44CvzNRo\neowR0bLhlUa1vVPSucD1wAJgve0tkt5Wnl9r+yuSrgPuBiaBy2zfO1O7SYwR0a4hr65jeyOwsXZs\nbW3/YuDipm0mMUZEqwx4YrTffElijIjWjforgUmMEdGu5hMrnUlijIjWDfAcYyeSGCOidekxRkRU\njMOyY0mMEdEuG2eh2oiIXqn5EhFRk6F0RERV6kpHRPTK5EtExG7M5MRo32RMYoyIdmUoHRExjSTG\niIheI54X21vBW9L7JL2zsv/7kt4h6WJJ90q6R9Kp5bkTJX2mcu2HJZ3ZVqwRMXemJl+GtIL3nGiz\ntMF64AwASftQLEG+HTiKov7CK4CLJR0ySKOS1kxVEBtyvBExF8piWE22rrQ2lLb9dUmPSHoJ8Fzg\nDuAE4ErbE8C/SvoicCzw/QHaXQesA5A04h30iAAzmVcCe1wGnAn8R4oe5El9rttJb2/2WXMbVkS0\nadRnpduuEngNsIqiV3g9cBNwqqQFkg4Gfhm4FXgQWCbpmZIOpKgHGxHzhd1s60irPUbbT0m6Efie\n7QlJ1wD/GbiL4p7s+bb/P4CkTwD3Ag9QDLsjYh6ws1Btj3LS5Xjg9QAu+tPvLrcets8Hzm8zvoho\nx4iPpFt9XGcZsA34J9v/r63vjYhR0+xRnS7vQ7Y5K70V+IW2vi8iRpTJrHRERJXJPcaIiN2M+uM6\nSYwR0bJuH8VpIokxItqVZcciInY3OZHEGBGxS0obRETUZSgdEVHX7cPbTSQxRkTrkhgjImpG/QHv\ntpcdi4ifclOr6wxrBW9JqyTdJ2mbpAtnuO5YSTsl/fpsbSYxRkTrhrWIhKQFwCXAamAZcHq5YM10\n110E3NAkviTGiGjZUFfXWQlss32/7aeAq4BTprnu7cA/AN9u0mgSY0S0a7hD6UXAQ5X97eWxXSQt\nAl4LfKRpiJl8iUYkdR3CgMYn3onJia5DaGzlsccOpZ0BZqUX1iqArisL4A3iz4ALbE82/T1OYoyI\nVg345ssO2ytmOP8wcGhlf3F5rGoFcFWZFBcCr5K00/a1/RpNYoyIlhkPb6Ha24Clkg6nSIinAW/s\n+Tb78KnPki4HPjNTUoQkxohom8FDyou2d0o6l6Lq6AJgve0tkt5Wnl+7J+0mMUZE64b55ovtjcDG\n2rFpE6LtM5u0mcQYEa3LK4ERERVZdiwios5mciJVAiMieqXHGBHRyyQxRkTs4qzgHRFRZzysBxnn\nSBJjRLQuPcaIiJrJ4b0SOCeSGCOiVcVai0mMERG9MpSOiOiVx3UiImoy+TIEkt4L/JvtP+46lojY\nW2ZyxFctH4vEGBHzRx7wjoiYRhJjRERNEuMck7QGWNN1HBHRlPO4zjDYfu8M59YB6wAkjfafdkQA\nYPKAd0TELnZeCRyKsuLXD2xf0XUsEbG3nHuMw7CnJRAjYjTlXemIiJr0GCMiapIYIyKqnMd1IiJ6\nGJh03pWOiKjIrHRExG6SGCMiapIYIyIqirmXPMcYEVFhnFcCIyJ6peZLRERN7jFGRPRIXemIiB7j\nUPNln64DiIifPrYbbU1IWiXpPknbJF04zfk3Sbpb0j2Sbpa0fLY202OMiNYNa6FaSQuAS4CTgO3A\nbZI22N5auewB4FdsPyppNcWK/8fN1G4SY0S0zDC8e4wrgW227weQdBVwCrArMdq+uXL9LcDi2RpN\nYox5arTvYVXtI3UdQusGeFxnoaRNlf11ZZ2nKYuAhyr725m5N/gW4LOzfWkSY0S0asDJlx22Vwzj\neyW9jCIxnjDbtUmMEdG6Ic5KPwwcWtlfXB7rIelI4DJgte1HZms0iTEiWjbU5xhvA5ZKOpwiIZ4G\nvLF6gaTDgKuBN9v+WpNGkxgjonXDmpW2vVPSucD1wAJgve0tZWXRqUJ67wF+FrhUxf3cnbMNz5MY\nI6JVw37A2/ZGYGPt2NrK57OBswdpM4kxIlqWmi8REbsxeVc6IqLHqL8rncQYES3z0CZf5koSY0S0\nKqUNIiKmkaF0RERNEmNERI88rhMRsZsUw4qIqLBhcnKi6zBmlMQYES1rXragK0mMEdG6JMaIiJpR\nT4x7XSVQ0hfKCl13ltsnK+fWSPpqud0q6YTKuZMl3SHpLklbJb11b2OJiPFgTzbaurJHPUZJzwCe\nbvuJ8tCbbG+qXXMy8FbgBNs7JB0NXCtpJfAIRaWulba3S3omsKT8uYNsP7pn/zoRMfI8+o/rDNRj\nlPSLkv4EuA94/iyXXwC82/YOANu3Ax8HzgH2p0jKj5TnnrR9X/lzp0q6V9L/lnTwIPFFxOgzMOnJ\nRltXZk2MkvaTdJakfwb+kqIs4ZG276hc9reVofTF5bEjgM215jYBR9j+LrABeFDSlWVB7H1g1wKT\nq4F9gS9J+mRZUHvaWMvh+qZaJbGIGGHzYSj9LeBu4GzbX+1zzW5D6dnYPlvSi4FXAO+iKJh9Znnu\nIeD3JL2fIkmup0iq/22adtZRDMuRNNr984hgHB7XaTKU/nWKIjNXS3qPpJ9v2PZW4JjasWOALVM7\ntu+x/acUSfF11QvLe5GXAh8CPgH8dsPvjYgRZ7vR1pVZE6PtG2yfCrwUeAz4lKTPS1oyy49+ALhI\n0s8CSDqKokd4qaTnSDqxcu1RwIPlda+UdDfwfuBGYJntd9reQkSMvamaL6OcGBvPSpe1WP8c+POy\nN1d9p+dvJf2w/LzD9itsb5C0CLi5HOI+DvyG7W9J2h84X9JHgR8CT1AOoykmZF5t+8G9+jeLiBFl\nPB9fCbR9a+XziTNc9xHgI9Mcfxx4VZ+fqU/YRMQ8k0UkIiJqRn3yJYkxIlqXxBgRUVFMrKTmS0RE\nj/QYIyJqUj41IqIuPcaIiCpj0mOMiNhl6s2XUZbEGBGtS2KMiKhJYoyI6OGUT42IqBqHe4x7XQwr\nImJgU3VfZtsaKFf4v0/SNkkXTnNekj5Unr+7rD81oyTGiGiZG/9vNpIWAJdQrPS/DDhd0rLaZauB\npeW2hmlW/KpLYoyI1g2x5stKYJvt+20/BVwFnFK75hTgChduAQ6UdMhMjeYeY0S0boivBC4CHqrs\nbweOa3DNIop6VtOab4lxB2WJhCFbWLY9DsYpVhiveOckVknDbnLKXMTbtObTTK6niK2JZ9UqgK4r\nC+DNqXmVGG3PSR1qSZtsr5iLtodtnGKF8Yp3nGKF0Y3X9qohNvcwcGhlf3F5bNBreuQeY0SMs9uA\npZIOl/QM4DSKmvVVG4Azytnp44HHbPcdRsM86zFGxE8X2zslnUsxPF8ArLe9RdLbyvNrgY0UNaa2\nAT8Azpqt3STGZub8nsYQjVOsMF7xjlOsMH7x7hHbGymSX/XY2spnA+cM0qZG/Qn0iIi25R5jRERN\nEmNERE0SY0RETRJjRERNEmNERE0SY0RETRJjRETNvwM+NCjPw8BKNAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc61bb642e8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 38m 44s (2100) 0.3325\n",
      "38m 44s (- 883m 46s) (2100 4%) 0.3377\n",
      "[log] 40m 36s (2200) 0.2391\n",
      "40m 36s (- 882m 12s) (2200 4%) 0.3217\n",
      "[log] 42m 25s (2300) 0.2144\n",
      "42m 25s (- 879m 49s) (2300 4%) 0.3013\n",
      "[log] 44m 14s (2400) 0.2987\n",
      "44m 15s (- 877m 38s) (2400 4%) 0.2743\n",
      "[log] 46m 4s (2500) 0.2795\n",
      "46m 4s (- 875m 27s) (2500 5%) 0.2610\n",
      "[log] 47m 53s (2600) 0.2676\n",
      "47m 53s (- 873m 0s) (2600 5%) 0.2380\n",
      "[log] 49m 43s (2700) 0.1816\n",
      "49m 43s (- 871m 10s) (2700 5%) 0.2199\n",
      "[log] 51m 35s (2800) 0.2438\n",
      "51m 35s (- 869m 43s) (2800 5%) 0.2171\n",
      "[log] 53m 25s (2900) 0.2003\n",
      "53m 25s (- 867m 44s) (2900 5%) 0.1989\n",
      "[log] 55m 16s (3000) 0.2235\n",
      "55m 16s (- 865m 59s) (3000 6%) 0.1900\n",
      "> choisis quelque chose .\n",
      "= choose something .\n",
      "< choose something . <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nrkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc5f742dd30>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 57m 8s (3100) 0.2420\n",
      "57m 8s (- 864m 34s) (3100 6%) 0.1877\n",
      "[log] 58m 57s (3200) 0.1424\n",
      "58m 57s (- 862m 22s) (3200 6%) 0.1783\n",
      "[log] 60m 50s (3300) 0.1371\n",
      "60m 50s (- 860m 59s) (3300 6%) 0.1750\n",
      "[log] 62m 41s (3400) 0.1539\n",
      "62m 41s (- 859m 21s) (3400 6%) 0.1679\n",
      "[log] 64m 30s (3500) 0.1167\n",
      "64m 30s (- 857m 8s) (3500 7%) 0.1695\n",
      "[log] 66m 23s (3600) 0.1849\n",
      "66m 23s (- 855m 44s) (3600 7%) 0.1630\n",
      "[log] 68m 16s (3700) 0.1372\n",
      "68m 16s (- 854m 25s) (3700 7%) 0.1544\n",
      "[log] 70m 8s (3800) 0.1163\n",
      "70m 8s (- 852m 44s) (3800 7%) 0.1434\n",
      "[log] 72m 0s (3900) 0.1499\n",
      "72m 0s (- 851m 7s) (3900 7%) 0.1415\n",
      "[log] 73m 51s (4000) 0.1129\n",
      "73m 51s (- 849m 18s) (4000 8%) 0.1405\n",
      "> nous sommes amoureux .\n",
      "= we re in love .\n",
      "< we re in love . <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc5d2d0df60>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 75m 43s (4100) 0.1106\n",
      "75m 43s (- 847m 48s) (4100 8%) 0.1315\n",
      "[log] 77m 34s (4200) 0.0593\n",
      "77m 34s (- 846m 0s) (4200 8%) 0.1353\n",
      "[log] 79m 27s (4300) 0.1601\n",
      "79m 27s (- 844m 29s) (4300 8%) 0.1256\n",
      "[log] 81m 17s (4400) 0.1076\n",
      "81m 17s (- 842m 29s) (4400 8%) 0.1285\n",
      "[log] 83m 8s (4500) 0.1967\n",
      "83m 8s (- 840m 42s) (4500 9%) 0.1237\n",
      "[log] 84m 59s (4600) 0.1156\n",
      "84m 59s (- 838m 49s) (4600 9%) 0.1175\n",
      "[log] 86m 51s (4700) 0.0809\n",
      "86m 51s (- 837m 13s) (4700 9%) 0.1118\n",
      "[log] 88m 41s (4800) 0.0821\n",
      "88m 41s (- 835m 13s) (4800 9%) 0.1115\n",
      "[log] 90m 32s (4900) 0.1044\n",
      "90m 32s (- 833m 18s) (4900 9%) 0.1140\n",
      "[log] 92m 23s (5000) 0.0773\n",
      "92m 23s (- 831m 35s) (5000 10%) 0.1076\n",
      "> qu ai je ?\n",
      "= what do i have ?\n",
      "< what do i have ? <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nHSqs1C56AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc5ae7885c0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 94m 14s (5100) 0.0806\n",
      "94m 14s (- 829m 41s) (5100 10%) 0.1055\n",
      "[log] 96m 6s (5200) 0.0678\n",
      "96m 6s (- 827m 57s) (5200 10%) 0.1025\n",
      "[log] 97m 59s (5300) 0.1065\n",
      "97m 59s (- 826m 30s) (5300 10%) 0.1026\n",
      "[log] 99m 49s (5400) 0.1059\n",
      "99m 49s (- 824m 29s) (5400 10%) 0.1007\n",
      "[log] 101m 40s (5500) 0.1084\n",
      "101m 40s (- 822m 41s) (5500 11%) 0.0991\n",
      "[log] 103m 32s (5600) 0.1498\n",
      "103m 32s (- 820m 54s) (5600 11%) 0.0985\n",
      "[log] 105m 23s (5700) 0.0675\n",
      "105m 23s (- 819m 6s) (5700 11%) 0.1009\n",
      "[log] 107m 15s (5800) 0.1340\n",
      "107m 15s (- 817m 21s) (5800 11%) 0.0985\n",
      "[log] 109m 7s (5900) 0.0902\n",
      "109m 7s (- 815m 39s) (5900 11%) 0.0970\n",
      "[log] 110m 59s (6000) 0.0942\n",
      "110m 59s (- 813m 57s) (6000 12%) 0.0997\n",
      "> vous plaisantez ?\n",
      "= are you kidding ?\n",
      "< are you kidding ? <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc58a014ef0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 112m 52s (6100) 0.0712\n",
      "112m 52s (- 812m 17s) (6100 12%) 0.0981\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-30-38d214f6947a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     15\u001b[0m         \u001b[0minput_batches\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minput_lengths\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget_batches\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget_lengths\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     16\u001b[0m         \u001b[0mencoder\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdecoder\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m         \u001b[0mencoder_optimizer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdecoder_optimizer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcriterion\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     18\u001b[0m     )\n\u001b[1;32m     19\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-22-7d51dc014236>\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(input_batches, input_lengths, target_batches, target_lengths, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length)\u001b[0m\n\u001b[1;32m     36\u001b[0m         \u001b[0mtarget_lengths\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     37\u001b[0m     )\n\u001b[0;32m---> 38\u001b[0;31m     \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     39\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     40\u001b[0m     \u001b[0;31m# Clip gradient norms\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/home/sean/anaconda3/lib/python3.6/site-packages/torch/autograd/variable.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph, retain_variables)\u001b[0m\n\u001b[1;32m    149\u001b[0m             \u001b[0mDefaults\u001b[0m \u001b[0mto\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munless\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mgradient\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0ma\u001b[0m \u001b[0mvolatile\u001b[0m \u001b[0mVariable\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    150\u001b[0m         \"\"\"\n\u001b[0;32m--> 151\u001b[0;31m         \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_variables\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    152\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    153\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/home/sean/anaconda3/lib/python3.6/site-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(variables, grad_variables, retain_graph, create_graph, retain_variables)\u001b[0m\n\u001b[1;32m     96\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     97\u001b[0m     Variable._execution_engine.run_backward(\n\u001b[0;32m---> 98\u001b[0;31m         variables, grad_variables, retain_graph)\n\u001b[0m\u001b[1;32m     99\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    100\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# Begin!\n",
    "ecs = []\n",
    "dcs = []\n",
    "eca = 0\n",
    "dca = 0\n",
    "\n",
    "while epoch < n_epochs:\n",
    "    epoch += 1\n",
    "    \n",
    "    # Get training data for this cycle\n",
    "    input_batches, input_lengths, target_batches, target_lengths = random_batch(batch_size)\n",
    "\n",
    "    # Run the train function\n",
    "    loss, ec, dc = train(\n",
    "        input_batches, input_lengths, target_batches, target_lengths,\n",
    "        encoder, decoder,\n",
    "        encoder_optimizer, decoder_optimizer, criterion\n",
    "    )\n",
    "\n",
    "    # Keep track of loss\n",
    "    print_loss_total += loss\n",
    "    plot_loss_total += loss\n",
    "    eca += ec\n",
    "    dca += dc\n",
    "    \n",
    "    job.record(epoch, loss)\n",
    "\n",
    "    if epoch % print_every == 0:\n",
    "        print_loss_avg = print_loss_total / print_every\n",
    "        print_loss_total = 0\n",
    "        print_summary = '%s (%d %d%%) %.4f' % (time_since(start, epoch / n_epochs), epoch, epoch / n_epochs * 100, print_loss_avg)\n",
    "        print(print_summary)\n",
    "        \n",
    "    if epoch % evaluate_every == 0:\n",
    "        evaluate_randomly()\n",
    "\n",
    "    if epoch % plot_every == 0:\n",
    "        plot_loss_avg = plot_loss_total / plot_every\n",
    "        plot_losses.append(plot_loss_avg)\n",
    "        plot_loss_total = 0\n",
    "        \n",
    "        # TODO: Running average helper\n",
    "        ecs.append(eca / plot_every)\n",
    "        dcs.append(dca / plot_every)\n",
    "        ecs_win = 'encoder grad (%s)' % hostname\n",
    "        dcs_win = 'decoder grad (%s)' % hostname\n",
    "        vis.line(np.array(ecs), win=ecs_win, opts={'title': ecs_win})\n",
    "        vis.line(np.array(dcs), win=dcs_win, opts={'title': dcs_win})\n",
    "        eca = 0\n",
    "        dca = 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting training loss\n",
    "\n",
    "Plotting is done with matplotlib, using the array `plot_losses` that was created while training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc5852c0a20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc585256240>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def show_plot(points):\n",
    "    plt.figure()\n",
    "    fig, ax = plt.subplots()\n",
    "    loc = ticker.MultipleLocator(base=0.2) # put ticks at regular intervals\n",
    "    ax.yaxis.set_major_locator(loc)\n",
    "    plt.plot(points)\n",
    "\n",
    "show_plot(plot_losses)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "output_words, attentions = evaluate(\"je suis trop froid .\")\n",
    "plt.matshow(attentions.numpy())\n",
    "show_plot_visdom()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"elle a cinq ans de moins que moi .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"elle est trop petit .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"je ne crains pas de mourir .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"c est un jeune directeur plein de talent .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"est le chien vert aujourd hui ?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"le chat me parle .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"des centaines de personnes furent arretees ici .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"des centaines de chiens furent arretees ici .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "evaluate_and_show_attention(\"ce fromage est prepare a partir de lait de chevre .\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Exercises\n",
    "\n",
    "* Try with a different dataset\n",
    "    * Another language pair\n",
    "    * Human &rarr; Machine (e.g. IOT commands)\n",
    "    * Chat &rarr; Response\n",
    "    * Question &rarr; Answer\n",
    "* Replace the embedding pre-trained word embeddings such as word2vec or GloVe\n",
    "* Try with more layers, more hidden units, and more sentences. Compare the training time and results.\n",
    "* If you use a translation file where pairs have two of the same phrase (`I am test \\t I am test`), you can use this as an autoencoder. Try this:\n",
    "    * Train as an autoencoder\n",
    "    * Save only the Encoder network\n",
    "    * Train a new Decoder for translation from there"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
